﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Castle.Facilities;
using Castle.Facilities.Startable;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using Castle.Windsor;
using Topics.Radical.Linq;
using SampleApp.MvcBackend.ComponentModel;

namespace SampleApp.MvcBackend.Boot
{
    public class Bootstrapper : IDisposable, IHoldBootstrapInformation
    {
        ~Bootstrapper()
        {
            this.Dispose( false );
        }

        void Dispose( Boolean disposing )
        {
            if ( disposing )
            {

                if ( this.windsorContainer != null )
                {
                    this.windsorContainer.Dispose();
                }

                if ( this.aggregateCatalog != null )
                {
                    this.aggregateCatalog.Dispose();
                }

                if ( this.mefContainer != null )
                {
                    this.mefContainer.Dispose();
                }
            }

            this.windsorContainer = null;
            this.aggregateCatalog = null;
            this.mefContainer = null;
            this.applicationObjects = null;
        }

        public void Dispose()
        {
            this.Dispose( true );
            GC.SuppressFinalize( this );
        }

        static readonly Object syncRoot = new Object();

        Boolean _isInitialized;

        IWindsorContainer windsorContainer;
        CompositionContainer mefContainer;
        AggregateCatalog aggregateCatalog;
        String directory;

        public Bootstrapper( string directory, String defaultAssemblyFileSelectPattern = "SampleApp.*.dll" )
        {
            this.directory = directory;

            this.aggregateCatalog = new AggregateCatalog( new DirectoryCatalog( this.directory, defaultAssemblyFileSelectPattern ) );
        }

        [ImportMany]
        IEnumerable<IWindsorInstaller> Installers { get; set; }

        [ImportMany]
        IEnumerable<IFacility> Facilities { get; set; }

        public String GetBootDirectory()
        {
            return this.directory;
        }

        public IWindsorContainer GetContainer()
        {
            return this.windsorContainer;
        }

        public IWindsorContainer Boot()
        {
            if ( !this._isInitialized )
            {
                lock ( syncRoot )
                {
                    if ( !this._isInitialized )
                    {
                        this.windsorContainer = new Castle.Windsor.WindsorContainer();
						this.windsorContainer.AddFacility<StartableFacility>();
						this.windsorContainer.AddFacility<SubscribeToMessageFacility>();

                        this.windsorContainer.Kernel.Resolver.AddSubResolver( new ArrayResolver( this.windsorContainer.Kernel, true ) );

                        try
                        {
                            this.mefContainer = new CompositionContainer( this.aggregateCatalog );
                            this.mefContainer.ComposeParts( this );
                        }
                        catch ( ReflectionTypeLoadException tpe )
                        {
                            foreach ( var e in tpe.LoaderExceptions )
                            {
                                Debug.WriteLine( e.Message );
                            }

                            throw;
                        }

                        foreach ( var facility in this.Facilities )
                        {
                            this.windsorContainer.AddFacility( facility );
                        }

                        this.windsorContainer.Register
                        (
                            Component.For<Bootstrapper, IHoldBootstrapInformation>().Instance( this )
                        );

                        this.windsorContainer.Register
                        (
                            Component.For<CompositionContainer>().Instance( this.mefContainer )
                        );

                        this.windsorContainer.Install( this.Installers.ToArray() );

						//this.windsorContainer.ResolveAll<IExpectBootCompletedNotification>().ForEach( n => n.OnBootCompleted() );

                        this._isInitialized = true;
                    }
                }
            }

            return this.windsorContainer;
        }

        public void Shutdown()
        {
            this.applicationObjects.Clear();

            this.Dispose();
        }

        public void AddCatalog( ComposablePartCatalog catalog )
        {
            this.aggregateCatalog.Catalogs.Add( catalog );
        }

        Dictionary<String, Object> applicationObjects = new Dictionary<string, object>();

        public void AddApplicationObject( string identifier, object instance )
        {
            this.applicationObjects.Add( identifier, instance );
        }

        public T GetApplicationObject<T>( string identifier )
        {
            return ( T )this.applicationObjects[ identifier ];
        }

        public CompositionContainer GetCompositionContainer()
        {
            return this.mefContainer;
        }
    }
}
